package org.example.analysis.dao;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.example.analysis.model.entity.*;
import org.example.analysis.util.DatabaseUtil;

import java.sql.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;

/**
 * 代码分析数据访问层
 */
@Slf4j
public class CodeAnalysisDao {
    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 保存项目信息
     */
    public void saveProjectInfo(ProjectInfo projectInfo) {
        String sql = "INSERT INTO project_info (project_name, project_path, description, last_analyzed_time, refreshable) " +
                "VALUES (?, ?, ?, ?, ?)";
        
        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, projectInfo.getProjectName());
            pstmt.setString(2, projectInfo.getProjectPath());
            pstmt.setString(3, projectInfo.getDescription());
            pstmt.setTimestamp(4, projectInfo.getLastAnalysisTime() != null ?
                    new Timestamp(projectInfo.getLastAnalysisTime().getTime()) : null);
            pstmt.setBoolean(5, projectInfo.getRefreshable());
            
            pstmt.executeUpdate();
            log.info("保存项目信息成功: {}", projectInfo.getProjectName());
        } catch (SQLException e) {
            log.error("保存项目信息失败: {}", projectInfo.getProjectName(), e);
        }
    }

    /**
     * 保存类信息
     */
    public void saveClassInfo(JavaClassInfo classInfo) {
        try (Connection conn = DatabaseUtil.getConnection()) {
            // 开始事务
            conn.setAutoCommit(false);
            
            try {
                // 先删除旧的类信息（包括关联的所有数据）
                deleteClassInfo(conn, classInfo.getProjectName(), classInfo.getFullQualifiedName());
                
                // 保存新的类信息
                String sql = "INSERT INTO class_info (project_name, class_name, package_name, class_comment, " +
                        "full_qualified_name, super_class, is_interface, is_abstract, code_lines, " +
                        "last_modified_time, last_modified_by, refreshable, class_type) " +
                        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
                
                try (PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
                    pstmt.setString(1, classInfo.getProjectName());
                    pstmt.setString(2, classInfo.getClassName());
                    pstmt.setString(3, classInfo.getPackageName());
                    pstmt.setString(4, classInfo.getClassComment());
                    pstmt.setString(5, classInfo.getFullQualifiedName());
                    pstmt.setString(6, classInfo.getSuperClass());
                    pstmt.setBoolean(7, classInfo.isInterface());
                    pstmt.setBoolean(8, classInfo.isAbstract());
                    pstmt.setInt(9, classInfo.getCodeLines());
                    pstmt.setTimestamp(10, classInfo.getLastModifiedTime() != null ?
                            new Timestamp(classInfo.getLastModifiedTime().getTime()) : null);
                    pstmt.setString(11, classInfo.getLastModifiedBy());
                    pstmt.setBoolean(12, classInfo.getRefreshable());
                    pstmt.setString(13, classInfo.getClassType());
                    
                    pstmt.executeUpdate();
                }
                
                // 提交事务
                conn.commit();
                log.info("保存类信息成功: {}", classInfo.getClassName());
            } catch (Exception e) {
                // 回滚事务
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    log.error("回滚事务失败", ex);
                }
                log.error("保存类信息失败: {}", classInfo.getClassName(), e);
            }
        } catch (SQLException e) {
            log.error("获取数据库连接失败", e);
        }
    }

    /**
     * 删除类信息（包括关联的所有数据）
     */
    private void deleteClassInfo(Connection conn, String projectName, String fullQualifiedName) {
        // 先获取类ID
        String selectSql = "SELECT id FROM class_info WHERE project_name = ? AND full_qualified_name = ?";
        try (PreparedStatement selectStmt = conn.prepareStatement(selectSql)) {
            selectStmt.setString(1, projectName);
            selectStmt.setString(2, fullQualifiedName);
            try (ResultSet rs = selectStmt.executeQuery()) {
                if (rs.next()) {
                    long classId = rs.getLong("id");
                    
                    // 删除方法参数
                    try {
                        String deleteParamsSql = "DELETE FROM parameter_info WHERE project_name = ? AND method_id IN " +
                                              "(SELECT id FROM method_info WHERE class_id = ?)";
                        try (PreparedStatement deleteParamsStmt = conn.prepareStatement(deleteParamsSql)) {
                            deleteParamsStmt.setString(1, projectName);
                            deleteParamsStmt.setLong(2, classId);
                            deleteParamsStmt.executeUpdate();
                        }
                    } catch (SQLException e) {
                        log.error("删除方法参数失败: classId={}", classId, e);
                    }
                    
                    // 删除方法信息
                    try {
                        String deleteMethodsSql = "DELETE FROM method_info WHERE project_name = ? AND class_id = ?";
                        try (PreparedStatement deleteMethodsStmt = conn.prepareStatement(deleteMethodsSql)) {
                            deleteMethodsStmt.setString(1, projectName);
                            deleteMethodsStmt.setLong(2, classId);
                            deleteMethodsStmt.executeUpdate();
                        }
                    } catch (SQLException e) {
                        log.error("删除方法信息失败: classId={}", classId, e);
                    }
                    
                    // 删除字段信息
                    try {
                        String deleteFieldsSql = "DELETE FROM field_info WHERE project_name = ? AND class_id = ?";
                        try (PreparedStatement deleteFieldsStmt = conn.prepareStatement(deleteFieldsSql)) {
                            deleteFieldsStmt.setString(1, projectName);
                            deleteFieldsStmt.setLong(2, classId);
                            deleteFieldsStmt.executeUpdate();
                        }
                    } catch (SQLException e) {
                        log.error("删除字段信息失败: classId={}", classId, e);
                    }
                    
                    // 删除接口信息
                    try {
                        String deleteInterfacesSql = "DELETE FROM class_interface WHERE project_name = ? AND class_id = ?";
                        try (PreparedStatement deleteInterfacesStmt = conn.prepareStatement(deleteInterfacesSql)) {
                            deleteInterfacesStmt.setString(1, projectName);
                            deleteInterfacesStmt.setLong(2, classId);
                            deleteInterfacesStmt.executeUpdate();
                        }
                    } catch (SQLException e) {
                        log.error("删除接口信息失败: classId={}", classId, e);
                    }
                    
                    // 删除类信息
                    try {
                        String deleteClassSql = "DELETE FROM class_info WHERE project_name = ? AND id = ?";
                        try (PreparedStatement deleteClassStmt = conn.prepareStatement(deleteClassSql)) {
                            deleteClassStmt.setString(1, projectName);
                            deleteClassStmt.setLong(2, classId);
                            deleteClassStmt.executeUpdate();
                        }
                    } catch (SQLException e) {
                        log.error("删除类信息失败: classId={}", classId, e);
                    }
                }
            }
        } catch (SQLException e) {
            log.error("查询类信息失败: projectName={}, fullQualifiedName={}", projectName, fullQualifiedName, e);
        }
    }

    /**
     * 通过项目编码获取项目信息
     *
     * @param projectName 项目编码
     * @return 项目信息
     */
    public ProjectInfo getProjectInfo(String projectName) {
        String sql = "SELECT id, project_name, project_path, description, last_analysis_time, refreshable, created_at " +
                "FROM project_info WHERE project_name = ?";
        
        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, projectName);
            
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    ProjectInfo projectInfo = new ProjectInfo()
                            .setId(rs.getInt("id"))
                            .setProjectName(rs.getString("project_name"))
                            .setProjectPath(rs.getString("project_path"))
                            .setDescription(rs.getString("description"))
                            .setRefreshable(rs.getBoolean("refreshable"));
                    
                    Timestamp lastAnalyzedAt = rs.getTimestamp("last_analysis_time");
                    if (lastAnalyzedAt != null) {
                        projectInfo.setLastAnalysisTime(new Date(lastAnalyzedAt.getTime()));
                    }
                    
                    Timestamp createdAt = rs.getTimestamp("created_at");
                    if (createdAt != null) {
                        projectInfo.setCreatedAt(new Date(createdAt.getTime()));
                    }
                    
                    return projectInfo;
                }
            }
        } catch (SQLException e) {
            log.error("获取项目信息失败: {}", projectName, e);
        }
        
        return null;
    }

    /**
     * 获取类信息
     *
     * @param projectName 项目名称
     * @param className 类名
     * @param packageName 包名
     * @return 类信息
     */
    public JavaClassInfo getClassInfo(String projectName, String className, String packageName) {
        String sql = "SELECT * FROM class_info WHERE project_name = ? AND class_name = ? AND package_name = ?";
        
        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, projectName);
            pstmt.setString(2, className);
            pstmt.setString(3, packageName);
            
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    JavaClassInfo classInfo = new JavaClassInfo()
                            .setId(rs.getLong("id"))
                            .setProjectName(rs.getString("project_name"))
                            .setClassName(rs.getString("class_name"))
                            .setPackageName(rs.getString("package_name"))
                            .setClassComment(rs.getString("class_comment"))
                            .setFullQualifiedName(rs.getString("full_qualified_name"))
                            .setSuperClass(rs.getString("super_class"))
                            .setInterface(rs.getBoolean("is_interface"))
                            .setAbstract(rs.getBoolean("is_abstract"))
                            .setCodeLines(rs.getInt("code_lines"))
                            .setClassType(rs.getString("class_type"))
                            .setRefreshable(rs.getBoolean("refreshable"));
                    
                    // 设置日期字段
                    Timestamp lastModifiedTime = rs.getTimestamp("last_modified_time");
                    if (lastModifiedTime != null) {
                        classInfo.setLastModifiedTime(new Date(lastModifiedTime.getTime()));
                    }
                    
                    classInfo.setLastModifiedBy(rs.getString("last_modified_by"));
                    
                    // 加载类的其他信息（接口、字段、方法等）
                    loadClassInterfaces(conn, classInfo);
                    loadClassFields(conn, classInfo);
                    loadClassMethods(conn, classInfo);
                    
                    return classInfo;
                }
            }
        } catch (SQLException e) {
            log.error("获取类信息失败: {}.{}", packageName, className, e);
        }
        
        return null;
    }
    
    /**
     * 加载类的接口信息
     */
    private void loadClassInterfaces(Connection conn, JavaClassInfo classInfo) throws SQLException {
        String sql = "SELECT interface_name FROM class_interface WHERE project_name = ? AND class_id = ?";
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, classInfo.getProjectName());
            pstmt.setLong(2, classInfo.getId());
            
            try (ResultSet rs = pstmt.executeQuery()) {
                while (rs.next()) {
                    classInfo.getInterfaces().add(rs.getString("interface_name"));
                }
            }
        }
    }
    
    /**
     * 加载类的字段信息
     */
    private void loadClassFields(Connection conn, JavaClassInfo classInfo) throws SQLException {
        String sql = "SELECT * FROM field_info WHERE project_name = ? AND class_id = ?";
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, classInfo.getProjectName());
            pstmt.setLong(2, classInfo.getId());
            
            try (ResultSet rs = pstmt.executeQuery()) {
                while (rs.next()) {
                    JavaFieldInfo fieldInfo = new JavaFieldInfo()
                            .setId(rs.getLong("id"))
                            .setProjectName(rs.getString("project_name"))
                            .setClassId(rs.getLong("class_id"))
                            .setFieldName(rs.getString("field_name"))
                            .setFieldType(rs.getString("field_type"))
                            .setFieldComment(rs.getString("field_comment"))
                            .setDefaultValue(rs.getString("default_value"))
                            .setRefreshable(rs.getBoolean("refreshable"));
                    
                    // 解析修饰符列表
                    String modifiersJson = rs.getString("modifiers");
                    if (modifiersJson != null) {
                        List<String> modifiers = objectMapper.readValue(modifiersJson, 
                                objectMapper.getTypeFactory().constructCollectionType(List.class, String.class));
                        fieldInfo.setModifiers(modifiers);
                    }
                    
                    classInfo.getFields().add(fieldInfo);
                }
            } catch (JsonMappingException e) {
                throw new RuntimeException(e);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    /**
     * 加载类的方法信息
     */
    private void loadClassMethods(Connection conn, JavaClassInfo classInfo) throws SQLException {
        String sql = "SELECT * FROM method_info WHERE project_name = ? AND class_id = ?";
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, classInfo.getProjectName());
            pstmt.setLong(2, classInfo.getId());
            
            try (ResultSet rs = pstmt.executeQuery()) {
                while (rs.next()) {
                    JavaMethodInfo methodInfo = new JavaMethodInfo()
                            .setId(rs.getLong("id"))
                            .setProjectName(rs.getString("project_name"))
                            .setClassId(rs.getLong("class_id"))
                            .setMethodName(rs.getString("method_name"))
                            .setMethodComment(rs.getString("method_comment"))
                            .setReturnType(rs.getString("return_type"))
                            .setBusinessLogic(rs.getString("business_logic"))
                            .setConstructor(rs.getBoolean("is_constructor"))
                            .setCodeLines(rs.getInt("code_lines"))
                            .setLastModifiedBy(rs.getString("last_modified_by"))
                            .setRefreshable(rs.getBoolean("refreshable"));
                    
                    // 设置日期字段
                    Timestamp lastModifiedTime = rs.getTimestamp("last_modified_time");
                    if (lastModifiedTime != null) {
                        methodInfo.setLastModifiedTime(new Date(lastModifiedTime.getTime()));
                    }
                    
                    // 解析修饰符列表
                    String modifiersJson = rs.getString("modifiers");
                    if (modifiersJson != null) {
                        List<String> modifiers = objectMapper.readValue(modifiersJson, 
                                objectMapper.getTypeFactory().constructCollectionType(List.class, String.class));
                        methodInfo.setModifiers(modifiers);
                    }
                    
                    classInfo.getMethods().add(methodInfo);
                }
            } catch (JsonMappingException e) {
                throw new RuntimeException(e);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 获取入口点信息
     */
    public EntryPointInfo getEntryPointInfo(String projectName, String className, String methodName, String entryType) {
        String sql = "SELECT * FROM entry_point_info WHERE project_name = ? AND class_name = ? AND method_name = ? AND entry_type = ?";
        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, projectName);
            pstmt.setString(2, className);
            pstmt.setString(3, methodName);
            pstmt.setString(4, entryType);
            
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    return new EntryPointInfo()
                        .setId(rs.getInt("id"))
                        .setProjectName(rs.getString("project_name"))
                        .setEntryType(rs.getString("entry_type"))
                        .setClassName(rs.getString("class_name"))
                        .setMethodName(rs.getString("method_name"))
                        .setEntryPath(rs.getString("entry_path"))
                        .setHttpMethod(rs.getString("http_method"))
                        .setMethodComment(rs.getString("method_comment"))
                        .setBusinessLogic(rs.getString("business_logic"))
                        .setLastModifiedTime(rs.getTimestamp("last_modified_time"))
                        .setLastModifiedBy(rs.getString("last_modified_by"))
                        .setCreatedAt(rs.getTimestamp("created_at"))
                        .setRefreshable(rs.getBoolean("refreshable"));
                }
                return null;
            }
        } catch (SQLException e) {
            log.error("查询入口点信息失败", e);
            return null;
        }
    }

    /**
     * 保存入口点信息
     */
    public boolean saveEntryPointInfo(EntryPointInfo info) {
        try (Connection conn = DatabaseUtil.getConnection()) {
            if (info.getId() != null) {
                // 更新
                String sql = "UPDATE entry_point_info SET " +
                        "project_name = ?, entry_type = ?, class_name = ?, method_name = ?, " +
                        "entry_path = ?, http_method = ?, method_comment = ?, business_logic = ?, " +
                        "last_modified_time = ?, last_modified_by = ?, refreshable = ? " +
                        "WHERE id = ?";
                try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
                    pstmt.setString(1, info.getProjectName());
                    pstmt.setString(2, info.getEntryType());
                    pstmt.setString(3, info.getClassName());
                    pstmt.setString(4, info.getMethodName());
                    pstmt.setString(5, info.getEntryPath());
                    pstmt.setString(6, info.getHttpMethod());
                    pstmt.setString(7, info.getMethodComment());
                    pstmt.setString(8, info.getBusinessLogic());
                    pstmt.setTimestamp(9, info.getLastModifiedTime() != null ? 
                            new Timestamp(info.getLastModifiedTime().getTime()) : null);
                    pstmt.setString(10, info.getLastModifiedBy());
                    pstmt.setBoolean(11, info.getRefreshable());
                    pstmt.setInt(12, info.getId());
                    
                    pstmt.executeUpdate();
                }
            } else {
                // 插入
                String sql = "INSERT INTO entry_point_info (" +
                        "project_name, entry_type, class_name, method_name, entry_path, " +
                        "http_method, method_comment, business_logic, last_modified_time, " +
                        "last_modified_by, created_at, refreshable,complexity) VALUES " +
                        "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)";
                try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
                    pstmt.setString(1, info.getProjectName());
                    pstmt.setString(2, info.getEntryType());
                    pstmt.setString(3, info.getClassName());
                    pstmt.setString(4, info.getMethodName());
                    pstmt.setString(5, info.getEntryPath());
                    pstmt.setString(6, info.getHttpMethod());
                    pstmt.setString(7, info.getMethodComment());
                    pstmt.setString(8, info.getBusinessLogic());
                    pstmt.setTimestamp(9, info.getLastModifiedTime() != null ? 
                            new Timestamp(info.getLastModifiedTime().getTime()) : null);
                    pstmt.setString(10, info.getLastModifiedBy());
                    pstmt.setTimestamp(11, info.getCreatedAt() != null ? 
                            new Timestamp(info.getCreatedAt().getTime()) : null);
                    pstmt.setBoolean(12, true);
                    pstmt.setInt(13, info.getComplexity());
                    
                    pstmt.executeUpdate();
                }
            }
            return true;
        } catch (SQLException e) {
            log.error("保存入口点信息失败", e);
            return false;
        }
    }

    /**
     * 更新项目信息
     */
    public boolean updateProjectInfo(ProjectInfo info) {
        String sql = "UPDATE project_info SET  last_analysis_time = ?" +
                "WHERE id = ?";
        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setTimestamp(1, info.getLastAnalysisTime() != null ?
                    new Timestamp(info.getLastAnalysisTime().getTime()) : null);
            pstmt.setInt(2, info.getId());
            
            pstmt.executeUpdate();
            return true;
        } catch (SQLException e) {
            log.error("更新项目信息失败", e);
            return false;
        }
    }

    public void deleteEntryPointInfoByProjectName(String projectName) {
        String sql = "DELETE FROM entry_point_info WHERE project_name = ?";
        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, projectName);
            pstmt.executeUpdate();
        } catch (SQLException e) {
            log.error("删除项目入口点信息失败", e);
        }
    }

    /**
     * 获取项目的所有入口点
     * @param projectName 项目名称
     * @return 入口点列表
     */
    public List<EntryPointInfo> getEntryPoints(String projectName) {
        try (Connection conn = DatabaseUtil.getConnection()) {
            String sql = "SELECT * FROM entry_point_info WHERE project_name = ?";
            try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
                pstmt.setString(1, projectName);
                try (ResultSet rs = pstmt.executeQuery()) {
                    List<EntryPointInfo> entryPoints = new ArrayList<>();
                    while (rs.next()) {
                        EntryPointInfo info = new EntryPointInfo();
                        info.setId(rs.getInt("id"));
                        info.setProjectName(rs.getString("project_name"));
                        info.setEntryType(rs.getString("entry_type"));
                        info.setClassName(rs.getString("class_name"));
                        info.setMethodName(rs.getString("method_name"));
                        info.setEntryPath(rs.getString("entry_path"));
                        info.setHttpMethod(rs.getString("http_method"));
                        info.setMethodComment(rs.getString("method_comment"));
                        info.setBusinessLogic(rs.getString("business_logic"));
                        info.setLastModifiedTime(rs.getTimestamp("last_modified_time"));
                        info.setLastModifiedBy(rs.getString("last_modified_by"));
                        info.setCreatedAt(rs.getTimestamp("created_at"));
                        info.setRefreshable(rs.getBoolean("refreshable"));
                        entryPoints.add(info);
                    }
                    return entryPoints;
                }
            }
        } catch (SQLException e) {
            log.error("获取入口点信息失败", e);
            return null;
        }
    }

    /**
     * 获取类的所有方法
     * @param conn 数据库连接
     * @param classId 类ID
     * @return 方法列表
     */
    private List<JavaMethodInfo> getMethodsByClassId(Connection conn, Long classId) throws SQLException {
        String sql = "SELECT * FROM method_info WHERE class_id = ?";
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setLong(1, classId);
            try (ResultSet rs = pstmt.executeQuery()) {
                List<JavaMethodInfo> methods = new ArrayList<>();
                while (rs.next()) {
                    JavaMethodInfo method = new JavaMethodInfo();
                    method.setId(rs.getLong("id"));
                    method.setClassId(classId);
                    method.setMethodName(rs.getString("method_name"));
                    method.setMethodComment(rs.getString("method_comment"));
                    method.setReturnType(rs.getString("return_type"));
                    method.setBusinessLogic(rs.getString("business_logic"));
                    method.setCodeLines(rs.getInt("code_lines"));
                    method.setLastModifiedTime(rs.getTimestamp("last_modified_time"));
                    method.setLastModifiedBy(rs.getString("last_modified_by"));
                    method.setCreatedAt(rs.getTimestamp("created_at"));
                    method.setRefreshable(rs.getBoolean("refreshable"));
                    methods.add(method);
                }
                return methods;
            }
        }
    }
}